/*
 * @(#) $Header$
 *
 * Copyright (C) 2010  Daniel Léonard
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

package ca.forklabs.crypto.md;


/**
 * This class embodies the algorithm for the message digest SHA-1 according to
 * <a target="external" href="http://www.itl.nist.gov/fipspubs/fip180-1.htm">FIPS 180-1</a>.
 * It is more elegant to get an instance with the factory method in class
 * {@link java.security.MessageDigest} :
 * <blockquote>
 * <code>MessageDigest sha1 = MessageDigest.getInstance(Fork.SHA_1, Fork.NAME);</code>
 * </blockquote>
 *
 * @author   <a href="mailto:forklabs at gmail.com?subject=ca.forklabs.crypto.md.SHA1">Daniel Léonard</a>
 * @version $Revision$
 * @see      ca.forklabs.crypto.Forklabs
 */
public class SHA1 extends SmallSHA {

// ----------------------------
//  Class variables
// ----------------------------

   /** The name of this message digest. */
   @SuppressWarnings("nls")
   public static final String NAME = "SHA-1";

   /** The length in bytes of the digest. */
   public static final int DIGEST_LEN = 20;

   /** The length in bytes of a block. */
   public static final int BUFFER_LEN = 64;

   /** The size of the H buffer. */
   public static final int H_LEN = 5;

   /** The size of the W buffer. */
   public static final int W_LEN = 80;


//----------------------------
// Constructor
//----------------------------

   /**
    * Constructor.
    */
   public SHA1() {
      super(SHA1.NAME, SHA1.BUFFER_LEN, SHA1.DIGEST_LEN, SHA1.H_LEN, SHA1.W_LEN);
      this.coreInit();
      }


//----------------------------
// Scrambling methods
//----------------------------

   private static int F1(int a, int b, int c) {
      return (c ^ (a & (b ^ c))) + 0x5a827999;
      }

   private static int F2(int a, int b, int c) {
      return (a ^ b ^ c) + 0x6ed9eba1;
      }

   private static int F3(int a, int b, int c) {
      return ((a & b) | (c & (a | b))) + 0x8f1bbcdc;
      }

   private static int F4(int a, int b, int c) {
      return (a ^ b ^ c) + 0xca62c1d6;
      }


//----------------------------
// Concrete core methods
//----------------------------

   @Override
   protected void coreTransform() {
      byte[] buffer = this.getBuffer();
      int[] w = this.getWBuffer();
      w[ 0] =  AbstractBlockMessageDigest.makeInt(buffer,  0,  1,  2,  3);
      w[ 1] =  AbstractBlockMessageDigest.makeInt(buffer,  4,  5,  6,  7);
      w[ 2] =  AbstractBlockMessageDigest.makeInt(buffer,  8,  9, 10, 11);
      w[ 3] =  AbstractBlockMessageDigest.makeInt(buffer, 12, 13, 14, 15);
      w[ 4] =  AbstractBlockMessageDigest.makeInt(buffer, 16, 17, 18, 19);
      w[ 5] =  AbstractBlockMessageDigest.makeInt(buffer, 20, 21, 22, 23);
      w[ 6] =  AbstractBlockMessageDigest.makeInt(buffer, 24, 25, 26, 27);
      w[ 7] =  AbstractBlockMessageDigest.makeInt(buffer, 28, 29, 30, 31);
      w[ 8] =  AbstractBlockMessageDigest.makeInt(buffer, 32, 33, 34, 35);
      w[ 9] =  AbstractBlockMessageDigest.makeInt(buffer, 36, 37, 38, 39);
      w[10] =  AbstractBlockMessageDigest.makeInt(buffer, 40, 41, 42, 43);
      w[11] =  AbstractBlockMessageDigest.makeInt(buffer, 44, 45, 46, 47);
      w[12] =  AbstractBlockMessageDigest.makeInt(buffer, 48, 49, 50, 51);
      w[13] =  AbstractBlockMessageDigest.makeInt(buffer, 52, 53, 54, 55);
      w[14] =  AbstractBlockMessageDigest.makeInt(buffer, 56, 57, 58, 59);
      w[15] =  AbstractBlockMessageDigest.makeInt(buffer, 60, 61, 62, 63);
      for (int i = 16; i < 80; i++) {
         w[i] = AbstractBlockMessageDigest.rotateLeft(w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16], 1);
         }

      AbstractBlockMessageDigest.zeroize(buffer);

      int[] h = this.getHBuffer();
      int a = h[0];
      int b = h[1];
      int c = h[2];
      int d = h[3];
      int e = h[4];

   // cryptix block
      e += AbstractBlockMessageDigest.rotateLeft(a, 5) + SHA1.F1(b, c, d) + w[ 0]; b = AbstractBlockMessageDigest.rotateLeft(b, 30);
      d += AbstractBlockMessageDigest.rotateLeft(e, 5) + SHA1.F1(a, b, c) + w[ 1]; a = AbstractBlockMessageDigest.rotateLeft(a, 30);
      c += AbstractBlockMessageDigest.rotateLeft(d, 5) + SHA1.F1(e, a, b) + w[ 2]; e = AbstractBlockMessageDigest.rotateLeft(e, 30);
      b += AbstractBlockMessageDigest.rotateLeft(c, 5) + SHA1.F1(d, e, a) + w[ 3]; d = AbstractBlockMessageDigest.rotateLeft(d, 30);
      a += AbstractBlockMessageDigest.rotateLeft(b, 5) + SHA1.F1(c, d, e) + w[ 4]; c = AbstractBlockMessageDigest.rotateLeft(c, 30);
      e += AbstractBlockMessageDigest.rotateLeft(a, 5) + SHA1.F1(b, c, d) + w[ 5]; b = AbstractBlockMessageDigest.rotateLeft(b, 30);
      d += AbstractBlockMessageDigest.rotateLeft(e, 5) + SHA1.F1(a, b, c) + w[ 6]; a = AbstractBlockMessageDigest.rotateLeft(a, 30);
      c += AbstractBlockMessageDigest.rotateLeft(d, 5) + SHA1.F1(e, a, b) + w[ 7]; e = AbstractBlockMessageDigest.rotateLeft(e, 30);
      b += AbstractBlockMessageDigest.rotateLeft(c, 5) + SHA1.F1(d, e, a) + w[ 8]; d = AbstractBlockMessageDigest.rotateLeft(d, 30);
      a += AbstractBlockMessageDigest.rotateLeft(b, 5) + SHA1.F1(c, d, e) + w[ 9]; c = AbstractBlockMessageDigest.rotateLeft(c, 30);
      e += AbstractBlockMessageDigest.rotateLeft(a, 5) + SHA1.F1(b, c, d) + w[10]; b = AbstractBlockMessageDigest.rotateLeft(b, 30);
      d += AbstractBlockMessageDigest.rotateLeft(e, 5) + SHA1.F1(a, b, c) + w[11]; a = AbstractBlockMessageDigest.rotateLeft(a, 30);
      c += AbstractBlockMessageDigest.rotateLeft(d, 5) + SHA1.F1(e, a, b) + w[12]; e = AbstractBlockMessageDigest.rotateLeft(e, 30);
      b += AbstractBlockMessageDigest.rotateLeft(c, 5) + SHA1.F1(d, e, a) + w[13]; d = AbstractBlockMessageDigest.rotateLeft(d, 30);
      a += AbstractBlockMessageDigest.rotateLeft(b, 5) + SHA1.F1(c, d, e) + w[14]; c = AbstractBlockMessageDigest.rotateLeft(c, 30);
      e += AbstractBlockMessageDigest.rotateLeft(a, 5) + SHA1.F1(b, c, d) + w[15]; b = AbstractBlockMessageDigest.rotateLeft(b, 30);
      d += AbstractBlockMessageDigest.rotateLeft(e, 5) + SHA1.F1(a, b, c) + w[16]; a = AbstractBlockMessageDigest.rotateLeft(a, 30);
      c += AbstractBlockMessageDigest.rotateLeft(d, 5) + SHA1.F1(e, a, b) + w[17]; e = AbstractBlockMessageDigest.rotateLeft(e, 30);
      b += AbstractBlockMessageDigest.rotateLeft(c, 5) + SHA1.F1(d, e, a) + w[18]; d = AbstractBlockMessageDigest.rotateLeft(d, 30);
      a += AbstractBlockMessageDigest.rotateLeft(b, 5) + SHA1.F1(c, d, e) + w[19]; c = AbstractBlockMessageDigest.rotateLeft(c, 30);
      e += AbstractBlockMessageDigest.rotateLeft(a, 5) + SHA1.F2(b, c, d) + w[20]; b = AbstractBlockMessageDigest.rotateLeft(b, 30);
      d += AbstractBlockMessageDigest.rotateLeft(e, 5) + SHA1.F2(a, b, c) + w[21]; a = AbstractBlockMessageDigest.rotateLeft(a, 30);
      c += AbstractBlockMessageDigest.rotateLeft(d, 5) + SHA1.F2(e, a, b) + w[22]; e = AbstractBlockMessageDigest.rotateLeft(e, 30);
      b += AbstractBlockMessageDigest.rotateLeft(c, 5) + SHA1.F2(d, e, a) + w[23]; d = AbstractBlockMessageDigest.rotateLeft(d, 30);
      a += AbstractBlockMessageDigest.rotateLeft(b, 5) + SHA1.F2(c, d, e) + w[24]; c = AbstractBlockMessageDigest.rotateLeft(c, 30);
      e += AbstractBlockMessageDigest.rotateLeft(a, 5) + SHA1.F2(b, c, d) + w[25]; b = AbstractBlockMessageDigest.rotateLeft(b, 30);
      d += AbstractBlockMessageDigest.rotateLeft(e, 5) + SHA1.F2(a, b, c) + w[26]; a = AbstractBlockMessageDigest.rotateLeft(a, 30);
      c += AbstractBlockMessageDigest.rotateLeft(d, 5) + SHA1.F2(e, a, b) + w[27]; e = AbstractBlockMessageDigest.rotateLeft(e, 30);
      b += AbstractBlockMessageDigest.rotateLeft(c, 5) + SHA1.F2(d, e, a) + w[28]; d = AbstractBlockMessageDigest.rotateLeft(d, 30);
      a += AbstractBlockMessageDigest.rotateLeft(b, 5) + SHA1.F2(c, d, e) + w[29]; c = AbstractBlockMessageDigest.rotateLeft(c, 30);
      e += AbstractBlockMessageDigest.rotateLeft(a, 5) + SHA1.F2(b, c, d) + w[30]; b = AbstractBlockMessageDigest.rotateLeft(b, 30);
      d += AbstractBlockMessageDigest.rotateLeft(e, 5) + SHA1.F2(a, b, c) + w[31]; a = AbstractBlockMessageDigest.rotateLeft(a, 30);
      c += AbstractBlockMessageDigest.rotateLeft(d, 5) + SHA1.F2(e, a, b) + w[32]; e = AbstractBlockMessageDigest.rotateLeft(e, 30);
      b += AbstractBlockMessageDigest.rotateLeft(c, 5) + SHA1.F2(d, e, a) + w[33]; d = AbstractBlockMessageDigest.rotateLeft(d, 30);
      a += AbstractBlockMessageDigest.rotateLeft(b, 5) + SHA1.F2(c, d, e) + w[34]; c = AbstractBlockMessageDigest.rotateLeft(c, 30);
      e += AbstractBlockMessageDigest.rotateLeft(a, 5) + SHA1.F2(b, c, d) + w[35]; b = AbstractBlockMessageDigest.rotateLeft(b, 30);
      d += AbstractBlockMessageDigest.rotateLeft(e, 5) + SHA1.F2(a, b, c) + w[36]; a = AbstractBlockMessageDigest.rotateLeft(a, 30);
      c += AbstractBlockMessageDigest.rotateLeft(d, 5) + SHA1.F2(e, a, b) + w[37]; e = AbstractBlockMessageDigest.rotateLeft(e, 30);
      b += AbstractBlockMessageDigest.rotateLeft(c, 5) + SHA1.F2(d, e, a) + w[38]; d = AbstractBlockMessageDigest.rotateLeft(d, 30);
      a += AbstractBlockMessageDigest.rotateLeft(b, 5) + SHA1.F2(c, d, e) + w[39]; c = AbstractBlockMessageDigest.rotateLeft(c, 30);
      e += AbstractBlockMessageDigest.rotateLeft(a, 5) + SHA1.F3(b, c, d) + w[40]; b = AbstractBlockMessageDigest.rotateLeft(b, 30);
      d += AbstractBlockMessageDigest.rotateLeft(e, 5) + SHA1.F3(a, b, c) + w[41]; a = AbstractBlockMessageDigest.rotateLeft(a, 30);
      c += AbstractBlockMessageDigest.rotateLeft(d, 5) + SHA1.F3(e, a, b) + w[42]; e = AbstractBlockMessageDigest.rotateLeft(e, 30);
      b += AbstractBlockMessageDigest.rotateLeft(c, 5) + SHA1.F3(d, e, a) + w[43]; d = AbstractBlockMessageDigest.rotateLeft(d, 30);
      a += AbstractBlockMessageDigest.rotateLeft(b, 5) + SHA1.F3(c, d, e) + w[44]; c = AbstractBlockMessageDigest.rotateLeft(c, 30);
      e += AbstractBlockMessageDigest.rotateLeft(a, 5) + SHA1.F3(b, c, d) + w[45]; b = AbstractBlockMessageDigest.rotateLeft(b, 30);
      d += AbstractBlockMessageDigest.rotateLeft(e, 5) + SHA1.F3(a, b, c) + w[46]; a = AbstractBlockMessageDigest.rotateLeft(a, 30);
      c += AbstractBlockMessageDigest.rotateLeft(d, 5) + SHA1.F3(e, a, b) + w[47]; e = AbstractBlockMessageDigest.rotateLeft(e, 30);
      b += AbstractBlockMessageDigest.rotateLeft(c, 5) + SHA1.F3(d, e, a) + w[48]; d = AbstractBlockMessageDigest.rotateLeft(d, 30);
      a += AbstractBlockMessageDigest.rotateLeft(b, 5) + SHA1.F3(c, d, e) + w[49]; c = AbstractBlockMessageDigest.rotateLeft(c, 30);
      e += AbstractBlockMessageDigest.rotateLeft(a, 5) + SHA1.F3(b, c, d) + w[50]; b = AbstractBlockMessageDigest.rotateLeft(b, 30);
      d += AbstractBlockMessageDigest.rotateLeft(e, 5) + SHA1.F3(a, b, c) + w[51]; a = AbstractBlockMessageDigest.rotateLeft(a, 30);
      c += AbstractBlockMessageDigest.rotateLeft(d, 5) + SHA1.F3(e, a, b) + w[52]; e = AbstractBlockMessageDigest.rotateLeft(e, 30);
      b += AbstractBlockMessageDigest.rotateLeft(c, 5) + SHA1.F3(d, e, a) + w[53]; d = AbstractBlockMessageDigest.rotateLeft(d, 30);
      a += AbstractBlockMessageDigest.rotateLeft(b, 5) + SHA1.F3(c, d, e) + w[54]; c = AbstractBlockMessageDigest.rotateLeft(c, 30);
      e += AbstractBlockMessageDigest.rotateLeft(a, 5) + SHA1.F3(b, c, d) + w[55]; b = AbstractBlockMessageDigest.rotateLeft(b, 30);
      d += AbstractBlockMessageDigest.rotateLeft(e, 5) + SHA1.F3(a, b, c) + w[56]; a = AbstractBlockMessageDigest.rotateLeft(a, 30);
      c += AbstractBlockMessageDigest.rotateLeft(d, 5) + SHA1.F3(e, a, b) + w[57]; e = AbstractBlockMessageDigest.rotateLeft(e, 30);
      b += AbstractBlockMessageDigest.rotateLeft(c, 5) + SHA1.F3(d, e, a) + w[58]; d = AbstractBlockMessageDigest.rotateLeft(d, 30);
      a += AbstractBlockMessageDigest.rotateLeft(b, 5) + SHA1.F3(c, d, e) + w[59]; c = AbstractBlockMessageDigest.rotateLeft(c, 30);
      e += AbstractBlockMessageDigest.rotateLeft(a, 5) + SHA1.F4(b, c, d) + w[60]; b = AbstractBlockMessageDigest.rotateLeft(b, 30);
      d += AbstractBlockMessageDigest.rotateLeft(e, 5) + SHA1.F4(a, b, c) + w[61]; a = AbstractBlockMessageDigest.rotateLeft(a, 30);
      c += AbstractBlockMessageDigest.rotateLeft(d, 5) + SHA1.F4(e, a, b) + w[62]; e = AbstractBlockMessageDigest.rotateLeft(e, 30);
      b += AbstractBlockMessageDigest.rotateLeft(c, 5) + SHA1.F4(d, e, a) + w[63]; d = AbstractBlockMessageDigest.rotateLeft(d, 30);
      a += AbstractBlockMessageDigest.rotateLeft(b, 5) + SHA1.F4(c, d, e) + w[64]; c = AbstractBlockMessageDigest.rotateLeft(c, 30);
      e += AbstractBlockMessageDigest.rotateLeft(a, 5) + SHA1.F4(b, c, d) + w[65]; b = AbstractBlockMessageDigest.rotateLeft(b, 30);
      d += AbstractBlockMessageDigest.rotateLeft(e, 5) + SHA1.F4(a, b, c) + w[66]; a = AbstractBlockMessageDigest.rotateLeft(a, 30);
      c += AbstractBlockMessageDigest.rotateLeft(d, 5) + SHA1.F4(e, a, b) + w[67]; e = AbstractBlockMessageDigest.rotateLeft(e, 30);
      b += AbstractBlockMessageDigest.rotateLeft(c, 5) + SHA1.F4(d, e, a) + w[68]; d = AbstractBlockMessageDigest.rotateLeft(d, 30);
      a += AbstractBlockMessageDigest.rotateLeft(b, 5) + SHA1.F4(c, d, e) + w[69]; c = AbstractBlockMessageDigest.rotateLeft(c, 30);
      e += AbstractBlockMessageDigest.rotateLeft(a, 5) + SHA1.F4(b, c, d) + w[70]; b = AbstractBlockMessageDigest.rotateLeft(b, 30);
      d += AbstractBlockMessageDigest.rotateLeft(e, 5) + SHA1.F4(a, b, c) + w[71]; a = AbstractBlockMessageDigest.rotateLeft(a, 30);
      c += AbstractBlockMessageDigest.rotateLeft(d, 5) + SHA1.F4(e, a, b) + w[72]; e = AbstractBlockMessageDigest.rotateLeft(e, 30);
      b += AbstractBlockMessageDigest.rotateLeft(c, 5) + SHA1.F4(d, e, a) + w[73]; d = AbstractBlockMessageDigest.rotateLeft(d, 30);
      a += AbstractBlockMessageDigest.rotateLeft(b, 5) + SHA1.F4(c, d, e) + w[74]; c = AbstractBlockMessageDigest.rotateLeft(c, 30);
      e += AbstractBlockMessageDigest.rotateLeft(a, 5) + SHA1.F4(b, c, d) + w[75]; b = AbstractBlockMessageDigest.rotateLeft(b, 30);
      d += AbstractBlockMessageDigest.rotateLeft(e, 5) + SHA1.F4(a, b, c) + w[76]; a = AbstractBlockMessageDigest.rotateLeft(a, 30);
      c += AbstractBlockMessageDigest.rotateLeft(d, 5) + SHA1.F4(e, a, b) + w[77]; e = AbstractBlockMessageDigest.rotateLeft(e, 30);
      b += AbstractBlockMessageDigest.rotateLeft(c, 5) + SHA1.F4(d, e, a) + w[78]; d = AbstractBlockMessageDigest.rotateLeft(d, 30);
      a += AbstractBlockMessageDigest.rotateLeft(b, 5) + SHA1.F4(c, d, e) + w[79]; c = AbstractBlockMessageDigest.rotateLeft(c, 30);

      h[0] += a;
      h[1] += b;
      h[2] += c;
      h[3] += d;
      h[4] += e;

      AbstractBlockMessageDigest.zeroize(w);
      }

   @Override
   protected void coreInit() {
      int[] h = this.getHBuffer();
      h[0] = 0x67452301;
      h[1] = 0xefcdab89;
      h[2] = 0x98badcfe;
      h[3] = 0x10325476;
      h[4] = 0xc3d2e1f0;
      }

   }

